/**
 * @name <code>SimpleArray</code>
 * @author xjcc
 * @date 2009.09.22
 * @description
 * 		<code>SimpleArray</code> is used to store a group of 
 * 		single type data.
 * @constructor
 * 		SimpleArray(elementType:Class, numElements:uint)
 * 		SimpleArray(elementType:Class, ...args)
 * @useage
 * 		var sa:SimpleArray = new SimpleArray(int, 1, 2, 3);
 * 		sa.reverse();
 * 		trace(sa); //output 3,2,1
 */
package xjcc.core
{
//=====================================================================================
//                            imports
//=====================================================================================
	import flash.errors.IllegalOperationError;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;

	public dynamic class SimpleArray extends Proxy
	{
//=====================================================================================
//                            constructor
//=====================================================================================
		public function SimpleArray(elementType:Class, ...args)
		{
			this.elementType = elementType;
			array = [];
			(args.length==1&&(args[0] is uint))?length=args[0]:push.apply(this,args);
		}
//=====================================================================================
//                            static methods
//=====================================================================================
		public static function arrayToSimple(array:Array):SimpleArray
		{
			var classString:String = getQualifiedClassName(array[0]);
			var classReference:Class = getDefinitionByName(classString) as Class;
			var simpleArray:SimpleArray = new SimpleArray(classReference);
			var arrLength:uint = array.length;
			for (var i:int=0; i<arrLength; i++)
				simpleArray[i] = array[i];
			return simpleArray;
		}
		public static function simpleToArray(simpleArray:Array):Array
		{
			var array:Array = [];
			var arrLength:uint = simpleArray.length;
			for (var i:int=0; i<arrLength; i++)
				array[i] = simpleArray[i];
			return array;
		}
//=====================================================================================
//                            methods
//=====================================================================================
		public function copy():SimpleArray
		{
			var simpleArrayCopy:SimpleArray = new SimpleArray(__type);
			for (var i:int=__length-1; i>=0; i--)
				simpleArrayCopy[i] = array[i];
			return simpleArrayCopy;
		}
		public function concat(...args):SimpleArray
		{
			var simpleArrayCopy:SimpleArray = copy();
			var argsLength:uint = args.length;
			var startIndex:int = __length;
			for (var i:int=0; i<argsLength; i++)
				simpleArrayCopy[startIndex++] = (args[i] is __type)?args[i]:undefined;
			return simpleArrayCopy;
		}
		public function every(callback:Function, thisObject:* = null):Boolean
		{
			for (var all:* in array)
				if (!callback.call(thisObject, array[all], all, this)) return false;
			return true;
		}
		public function filter(callback:Function, thisObject:* = null):SimpleArray
		{
			var simpleArray:SimpleArray = new SimpleArray(__type);
			for (var i:int=0; i<__length; i++)
				if (callback.call(thisObject, array[i], i, this))
					simpleArray.push(array[i]);
			return simpleArray;
		}
		public function forEach(callback:Function, thisObject:* = null):void
		{
			for (var i:int=0; i<__length; i++)
				callback.call(thisObject, array[i], i, this);
		}
		public function indexOf(searchElement:*, fromIndex:int = 0):int
		{ return array.indexOf(searchElement, fromIndex); }
		public function join(sep:*=","):String
		{ return array.join(sep); }
		public function lastIndexOf(searchElement:*, fromIndex:int = 0x7fffffff):int
		{ return array.lastIndexOf(searchElement, fromIndex); }
		public function map(callback:Function, thisObject:* = null):SimpleArray
		{
			var classString:String = getQualifiedClassName 
				(callback.call(thisObject, array[0], 0, this));
			var classReference:Class = getDefinitionByName(classString) as Class;
			var simpleArray:SimpleArray = new SimpleArray(classReference, __length);
			for (var i:int=0; i<__length; i++)
				simpleArray[i] = callback.call(thisObject, array[i], i, this);
			return simpleArray;
		}
		public function pop():*
		{
			var temp:* = array.pop();
			updateLength();
			return temp;
		}
		public function push(...args):uint
		{
			var argsLength:uint = args.length;
			for (var i:uint=0; i<argsLength; i++)
				array[__length++] = (args[i] is __type)?args[i]:undefined;
			return __length;
		}
		public function reverse():SimpleArray
		{
			array.reverse();
			return this;
		}
		public function shift():*
		{
			var temp:* = array.shift();
			updateLength();
			return temp;
		}
		public function slice(startIndex:int = 0, endIndex:int = 16777215):SimpleArray
		{ return arrayToSimple(array.slice(startIndex, endIndex)); }
		public function some(callback:Function, thisObject:* = null):Boolean
		{
			for (var all:* in array)
				if (!callback.call(thisObject, array[all], all, this)) return true;
			return false;
		}
		public function sort(... args):SimpleArray
		{ return arrayToSimple(array.sort.apply(this, args)); }
		public function sortOn(fieldName:Object, options:Object = null):SimpleArray
		{ return arrayToSimple(array.sortOn(fieldName, options)); }
		public function splice(startIndex:int, deleteCount:uint, ...values):SimpleArray
		{
			(values as Array).unshift(startIndex, deleteCount);
			array.splice.apply(this, values);
			updateLength();
			return this;
		}
		public function toLocaleString():String
		{ return array.toLocaleString(); }
		public function toString():String
		{ return array.toString(); }
		public function unshift(...args):uint
		{
			var temp:uint = array.unshift.apply(this, args);
			updateLength();
			return temp;
		}
//=====================================================================================
//                            internal
//=====================================================================================
		override flash_proxy function setProperty(name:*, value:*):void
		{
			var index:int = parseInt(String(name));
			if (index >= 0)
				array[index] = (value==undefined||!(value is __type))?undefined:value;
			updateLength();
		}
		override flash_proxy function getProperty(name:*):*
		{ return array[name]; }
		override flash_proxy function callProperty(methodName:*, ...args):*
		{ throw new IllegalOperationError("method doesn't exist in SimpleArray!"); }
//=====================================================================================
//                            private
//=====================================================================================
		private var array:Array;
		private var __length:uint = 0;
		private var __type:Class;
		private var __typeString:String;
		
		private function updateLength():void
		{ __length = array.length; }
//=====================================================================================
//                            getter/setters
//=====================================================================================
		public function get length():uint { return __length; }
		public function set length(value:uint):void
		{
			if (__length == value) return;
			array.length = __length = value;
		}
		
		public function get elementType():Class { return __type; }
		public function set elementType(value:Class):void
		{
			if (__type == value) return;
			__type = value;
			__typeString = __type.toString();
			__typeString = __typeString.substr(7, __typeString.length-8);
			if (__length != 0)
				for (var i:int=__length-1; i>=0; i--)
					array[i] = undefined;
				__length = 0;
		}
//=====================================================================================
//                            consts
//=====================================================================================
		public static const CASEINSENSITIVE:uint = 1;
		public static const DESCENDING:uint = 2;
		public static const NUMERIC:uint = 16;
		public static const RETURNINDEXEDARRAY:uint = 8;
		public static const UNIQUESORT:uint = 4;
	}
}